package com.postaop.hntl.front.iso8583;

import java.util.BitSet;
import java.util.Set;

import com.enlorenz.common.util.ByteArrayUtils;
import com.enlorenz.common.util.CodeUtils;
import com.enlorenz.common.util.MessageBuffer;
import com.enlorenz.common.util.StringUtils;

/**
 * 消息驱动器
 * 用于打包和解析消息
 * @author Karma
 */
public class MsgDriver implements IMsgPacker,IMsgParser{
	private boolean debug=false;
	//包头长度
	private int headLength=2;
	//字符编码
	private String encoding="UTF-8";
	
	//包头类型
	private IsoMsgHeadLengthType msgHeadLengthType
	=IsoMsgHeadLengthType.HEX_BIN;
	
	@Override
	public IsoMessage parse(IsoMsgTemplate isoMsgTemplate, byte[] data) {
		IsoMessage isoMessage=new IsoMessage();
		FieldCounter counter=new FieldCounter();
		
		//解析头长度
		if(msgHeadLengthType == IsoMsgHeadLengthType.HEX_BIN){
			/*byte [] buff=ByteAdd.subByte(data, 0, headLength);
			String headHex=CodeUtils.bytes2HexString(buff);*/
			counter.addCount(headLength);
		}else{
			throw new RuntimeException("不支持的包长度类型："+msgHeadLengthType);
		}
		
		//解析包头
		int isoHeadLength=isoMsgTemplate.getIsoHeadLength();
		byte [] isoHead=BinaryUtils.subBytes(data, isoHeadLength, counter);
		isoMessage.setIsoHead(CodeUtils.bytes2HexString(isoHead));
		
		
		//解析报文类型
		IsoElementProperty isoFieldProperty=
				isoMsgTemplate.getMsgTypeFieldProperty().getField();
		String msgType=parseFixedBytes(isoFieldProperty,isoFieldProperty.getLength(), data, counter);
		isoMessage.setMsgType(msgType);
		
		//解析位图域
		byte [] bitMap=BinaryUtils.subBytes(data, isoMsgTemplate.getBitMapLength(), counter);
		
		//解析内容域
		BitSet bitSet=BinaryUtils.getBitmap(bitMap);
		for(int i=0;i<bitSet.size();i++){
			if(bitSet.get(i)){
				IsoFieldProperty fieldProperty=isoMsgTemplate.getField(i);
				if(null == fieldProperty){
					throw new RuntimeException("第"+i+"位域无属性配置");
				}
				String parseData=parse(fieldProperty, data,counter);
				if(debug){
					String tmp="INX:【%s】：【%s】";
					String print=String.format(tmp, i,parseData);
					System.out.println(print);
				}
				isoMessage.setFieldValue(i, parseData);
			}
		}
		
		return isoMessage;
	}
	
	public String parse(IsoFieldProperty property,byte[] data,FieldCounter counter){
		IsoElementProperty field=property.getField();
		IsoMsgType msgType=field.getMsgType();
		//定长域
		if(msgType == IsoMsgType.FIXED){
			return parseFixedBytes(field,field.getLength(),data,counter);
		//变长域
		}else if(msgType == IsoMsgType.VAR){
			IsoElementProperty length=property.getLength();
			
			//解析长度域
			String lenValue=parseFixedBytes(length,length.getLength(),data, counter);
			Integer lenInt=Integer.valueOf(lenValue);
			
			//解析值域
			String value=parseFixedBytes(field,lenInt,data, counter);
			return value;
		}else{
			throw new RuntimeException("无法适配域类型"+field.getMsgType());
		}
	}

	private String parseFixedBytes(IsoElementProperty field,int length
			,byte[] data,FieldCounter counter) {
		IsoDataType dataType=field.getDataType();
		if(dataType == IsoDataType.ALPHA){
			byte [] value=BinaryUtils.subBytes(data, length, counter);
			return StringUtils.newString(value,encoding);
		}else if(dataType == IsoDataType.BIN){
			byte [] value=BinaryUtils.subBytes(data, length, counter);			
			String binData=CodeUtils.bytes2HexString(value);
			return binData;
		}else if(dataType == IsoDataType.BCD){
			//如果数据域为bcd，那么长度域的值很有可能为单数，必须变为整数
			if(length%2 == 1){
				length++;
			}
			length=length/2;
			byte [] value=BinaryUtils.subBytes(data, length, counter);
			String bcdData=CodeUtils.bytes2HexString(value);
			return bcdData;
		}else{
			throw new RuntimeException("无法适配域类型"+dataType);
		}
	}
	
	
	public byte [] pack(IsoMsgTemplate isoMsgTemplate,
			IsoMessage isoMessage){
		//字节数组缓存
		MessageBuffer messageBuffer=new MessageBuffer();
		
		//获取所有的域索引
		Set<Integer> indexSet=isoMessage.getIndexSet();
		
		for(Integer index:indexSet){
			IsoFieldProperty property=isoMsgTemplate.getField(index);
			if(null == property){
				throw new RuntimeException("第"+index+"位域无属性配置");
			}
			String stringValue=isoMessage.getFiledValue(index);
			//解析域
			byte [] bytesValue=parseField(isoMessage, stringValue, property);
			if(debug){
				String tmp="INX:【%s】：【%s】:ASC:【%s】:HEX:【%s】";
				String print=String.format(tmp, index,stringValue,new String(bytesValue),CodeUtils.bytes2HexString(bytesValue));
				System.out.println(print);
			}
			messageBuffer.append(bytesValue);
		}
		
		
		//组合剩余的报文
		return packAll(isoMsgTemplate, isoMessage, messageBuffer);
	}
	
	/**
	 * 打包所有的数据
	 * @param isoMsgTemplate 模板
	 * @param isoMessage 数据
	 * @param messageBuffer 消息体
	 * @return 整体报文
	 */
	private byte[] packAll(IsoMsgTemplate isoMsgTemplate,
			IsoMessage isoMessage, MessageBuffer messageBuffer) {
		
		MessageBuffer fullBuffer=new MessageBuffer();
		
		//报文头
		String isoHead=isoMessage.getIsoHead();
		if(null!=isoHead){
			fullBuffer.append(CodeUtils.hexString2Bytes(isoHead));
		}
		
		//消息类型
		byte[] msgTypeField=getFixedBytes(isoMessage.getMsgType(),
				isoMsgTemplate.getMsgTypeFieldProperty().getField());
		fullBuffer.append(msgTypeField);
		
		//位图
		BitSet bitSet=isoMessage.getBitSet();
		byte [] bitmapField=BinaryUtils.createBitmap(bitSet);
		fullBuffer.append(bitmapField);
		
		//消息体
		fullBuffer.append(messageBuffer.toByte());
		
		//获取全部报文体
		byte [] fullBytes=fullBuffer.toByte();
		
		//添加报文头长度
		if(msgHeadLengthType == IsoMsgHeadLengthType.HEX_BIN){
			byte [] buff=BinaryUtils.
					createHexBinaryLength(fullBytes.length, headLength);
			return ByteArrayUtils.byteAdd(buff,fullBytes);
		}else{
			throw new RuntimeException("不支持的包长度类型："+msgHeadLengthType);
		}
	}

	private byte [] parseField(IsoMessage isoMessage, String filedValue,
			IsoFieldProperty property) {
		IsoElementProperty field=property.getField();
		IsoElementProperty length=property.getLength();
		IsoMsgType msgType=field.getMsgType();
		//定长域
		if(msgType == IsoMsgType.FIXED){
			return getFixedBytes(filedValue, field);
		//变长域
		}else if(msgType == IsoMsgType.VAR){
			return getVarBytes(filedValue, field, length);
		}else{
			throw new RuntimeException("无法适配域类型"+msgType);
		}
	}

	private byte[] getVarBytes(String filedValue, IsoElementProperty field,
			IsoElementProperty length) {
		//生成值域
		byte [] byteValue=getFixedBytes(filedValue, field);
		//System.out.println(StringUtils.newString(byteValue,encoding));
		//System.out.println(CodeUtils.bytes2HexString(byteValue));
		
		//生成长度域
		byte [] lengthValue=createLength(filedValue,byteValue, length,field);
		return ByteArrayUtils.byteAdd(lengthValue, byteValue);
	}


	private byte[] getFixedBytes(String filedValue,
			IsoElementProperty property) {
		IsoDataType dataType=property.getDataType();

		if(dataType == IsoDataType.ALPHA){
			String value=getAlphaDefault(filedValue);
			byte [] result= StringUtils.getBytes(value,encoding);
			//补空格
			if(property.getMsgType() == IsoMsgType.FIXED){
				if(property.getFillDirectType() == IsoFillDirectType.RIGHT){
					if(property.getFillType() == IsoFillType.ZERO){
						result=ByteArrayUtils.appendRightBytes(result, property.getLength(), ByteArrayUtils.ZERO_BYTE);
					}else if(property.getFillType() == IsoFillType.BLANK){
						result=ByteArrayUtils.appendRightBytes(result, property.getLength(), ByteArrayUtils.BLANK_BYTE);
					}else if(property.getFillType() == IsoFillType.NULL_ZERO){
						result=ByteArrayUtils.appendRightBytes(result, property.getLength(), ByteArrayUtils.NULL_ZERO_BYTE);
					}
				}else if(property.getFillDirectType() == IsoFillDirectType.LEFT){
					if(property.getFillType() == IsoFillType.ZERO){
						result=ByteArrayUtils.appendLeftBytes(result, property.getLength(), ByteArrayUtils.ZERO_BYTE);
					}else if(property.getFillType() == IsoFillType.BLANK){
						result=ByteArrayUtils.appendLeftBytes(result, property.getLength(), ByteArrayUtils.BLANK_BYTE);
					}else if(property.getFillType() == IsoFillType.NULL_ZERO){
						result=ByteArrayUtils.appendLeftBytes(result, property.getLength(), ByteArrayUtils.NULL_ZERO_BYTE);
					}
				}else{
					throw new RuntimeException("不支持的补齐方式:"+property.getFillDirectType());
				}
			}
			return result;
		}else if(dataType == IsoDataType.BIN){
			if(property.getMsgType() == IsoMsgType.FIXED){
				
				if(property.getFillDirectType() == IsoFillDirectType.RIGHT){
					if(filedValue.length()%2 == 1){
						filedValue=filedValue+"0";
					}
					byte [] value=getBinDefault(filedValue, property.getLength());
					return BinaryUtils.appendRightBytes(value, property.getLength());
				}else if(property.getFillDirectType() == IsoFillDirectType.LEFT){
					if(filedValue.length()%2 == 1){
						filedValue="0"+filedValue;
					}
					byte [] value=getBinDefault(filedValue, property.getLength());
					return BinaryUtils.appendLeftBytes(value, property.getLength());
				}else{
					throw new RuntimeException("不支持的补齐方式:"+property.getFillDirectType());
				}
			}else{
				byte [] value=getBinDefault(filedValue,0);
				return value;
			}
		}else if(dataType == IsoDataType.BCD){
			String appendValue=filedValue;
			if(filedValue.length()%2 == 1){
				appendValue=filedValue+"0";
			}
			byte [] value=CodeUtils.hexString2Bytes(appendValue);
			
			if(property.getMsgType() == IsoMsgType.FIXED){
				if(property.getFillDirectType() == IsoFillDirectType.RIGHT){
					return BinaryUtils.appendRightBytes(value, property.getLength());
				}else if(property.getFillDirectType() == IsoFillDirectType.LEFT){
					return BinaryUtils.appendLeftBytes(value, property.getLength());
				}else{
					throw new RuntimeException("不支持的补齐方式:"+property.getFillDirectType());
				}
			}else{
				return value;
			}
		}else{
			throw new RuntimeException("无法适配域类型"+dataType);
		}
	}
	
	
	
	/**
	 * 获取属性值
	 * @param value
	 * @param property
	 * @return
	 */
	private byte [] createLength(String orgnValue,byte [] value,IsoElementProperty lenProperty
			,IsoElementProperty field){
		IsoDataType dataType=lenProperty.getDataType();
		int lengthValue=0;
		
		if(dataType == IsoDataType.ALPHA){
			lengthValue=value.length;
		}else if(dataType == IsoDataType.BIN){
			if(field.getDataType() == IsoDataType.BCD){
				lengthValue=orgnValue.length();
			}else{
				lengthValue=value.length;
			}
		}else{
			throw new RuntimeException("无法适配长度域类型"+dataType);
		}
		
		String lenStr=String.valueOf(lengthValue);
		if(lenStr.length()%2!=0){
			lenStr="0"+lenStr;
		}
		
		return getFixedBytes(lenStr, lenProperty);
	}
	
	private static byte [] getBinDefault(String object,int length){
		if(null == object){
			return new byte[length];
		}else{
			return CodeUtils.hexString2Bytes(object);
		}
	}
	
	private static String getAlphaDefault(String object){
		if(null == object){
			return "";
		}else{
			return object.toString();
		}
	}
	
	
	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}

	public int getHeadLength() {
		return headLength;
	}

	public void setHeadLength(int headLength) {
		this.headLength = headLength;
	}

	public IsoMsgHeadLengthType getMsgHeadLengthType() {
		return msgHeadLengthType;
	}

	public void setMsgHeadLengthType(IsoMsgHeadLengthType msgHeadLengthType) {
		this.msgHeadLengthType = msgHeadLengthType;
	}
	
	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	
}
